﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace LooseChart
{
    /// <summary>
    /// 折线图--未去重
    /// </summary>
    public class LineSeries : XYSeries
    {
        /// <summary>
        /// 是否显示折线点
        /// </summary>
        public bool ShowPoint
        {
            get { return (bool)GetValue(ShowPointProperty); }
            set { SetValue(ShowPointProperty, value); }
        }
        public static readonly DependencyProperty ShowPointProperty = DependencyProperty.Register("ShowPoint", typeof(bool), typeof(LineSeries), new PropertyMetadata(true, (d, e) => { if (d is ISeries series) series.Redraw(); }));

        public LineSeries() : base()
        {
        }

        /// <summary>
        /// 绘制内容
        /// </summary>
        /// <param name="color">基础颜色</param>
        /// <param name="list">矢量数据流</param>
        /// <param name="dc">画布</param>
        protected override void DrawDrawingContext(Color color, List<StreamGeometry> list, DrawingContext dc)
        {
            var pa = color.A / 255d;
            var fillbrush = new SolidColorBrush(Color.FromArgb(Convert.ToByte(FillColorA * pa), color.R, color.G, color.B));
            fillbrush.Freeze();
            var strokebrush = new SolidColorBrush(Color.FromArgb(Convert.ToByte(StrokeColorA * pa), color.R, color.G, color.B));
            strokebrush.Freeze();
            Brush fill = ShowFill ? fillbrush : null;
            Pen pen = ShowStroke ? new Pen(strokebrush, StrokeThickness) { DashStyle = base.DashStyle } : null;
            foreach (var stream in list)
            {
                if (stream.IsEmpty()) continue;
                dc.DrawGeometry(fill, pen, stream);
            }
        }

        /// <summary>
        /// 获取数据源的矢量数据流
        /// </summary>
        /// <param name="chartDataSource">数据源</param>
        /// <returns></returns>
        protected override async Task UpdateStreamGeometry(CancellationToken token)
        {
            if (!this.IsLoaded || token.IsCancellationRequested || XAxis == null || YAxis == null) return;
            var points = Statistics;
            if (points == null || points.Count <= 0)
            {
                MainGeometry = new List<StreamGeometry>();
                return;
            }
            var pointLength = PointLength;
            var halfPointLength = PointLength / 2d;
            var showPoint = this.ShowPoint;
            try
            {
                var temp = await Task.Factory.StartNew<List<StreamGeometry>>(() =>
                {
                    StreamGeometry stream = new StreamGeometry();
                    StreamGeometry linestream = new StreamGeometry();
                    using (StreamGeometryContext sgc = stream.Open())
                    {
                        using (StreamGeometryContext linesgc = linestream.Open())
                        {
                            bool firstpoint = true;
                            var datas = points.OrderBy(p => p.Value).Select(p => p.Key).ToArray();
                            foreach (var data in datas)
                            {
                                if (token.IsCancellationRequested) return null;
                                var xtemp = data.X;
                                var ytemp = data.Y;
                                if (showPoint)//显示折点
                                {
                                    var point = new Point(xtemp, ytemp);
                                    sgc.BeginFigure(point, true, true);
                                    sgc.PolyLineTo(new Point[] { new Point(xtemp + pointLength, ytemp), new Point(xtemp + pointLength, ytemp + pointLength), new Point(xtemp, ytemp + pointLength) }, true, true);
                                }
                                if (firstpoint)
                                {
                                    linesgc.BeginFigure(new Point(xtemp + halfPointLength, ytemp + halfPointLength), false, false);
                                    firstpoint = false;
                                }
                                else
                                {
                                    linesgc.LineTo(new Point(xtemp + halfPointLength, ytemp + halfPointLength), true, true);
                                }
                            }
                        }
                    }
                    stream.Freeze();
                    linestream.Freeze();
                    return new List<StreamGeometry>() { stream, linestream };
                }, token);
                if (!token.IsCancellationRequested) MainGeometry = temp;
            }
            catch (TaskCanceledException e)
            {
                Debug.WriteLine(string.Concat("折线图生成矢量图线程被取消", e));
            }
        }

        /// <summary>
        /// 统计点位数量信息
        /// </summary>
        /// <param name="dataSource">数据源</param>
        /// <param name="pointLength">点大小</param>
        /// <param name="width">显示区域宽度</param>
        /// <param name="height">显示区域高度</param>
        /// <param name="maxDegreeOfParallelism">最大线程数</param>
        /// <param name="xParam">x轴数据转换参数</param>
        /// <param name="yParam">y轴数据转换参数</param>
        /// <param name="xGetLocation">x轴获取数据位置方法</param>
        /// <param name="yGetLocation">y轴获取数据位置方法</param>
        /// <param name="excessDisplayAtEdge">超出界限是否压边显示</param>
        /// <returns></returns>
        protected override ConcurrentDictionary<Point, int> PointCountStatistics(
            CancellationToken token, byte pointLength, int maxDegreeOfParallelism, bool excessDisplayAtEdge,
            IList xSource, Func<object, double> xConverter, ValueLocationConvertParam xParam, Func<double, ValueLocationConvertParam, double> xGetLocation,
            IList ySource, Func<object, double> yConverter, ValueLocationConvertParam yParam, Func<double, ValueLocationConvertParam, double> yGetLocation)
        {
            if (xSource == null || ySource == null || token.IsCancellationRequested) return null;
            try
            {
                ConcurrentDictionary<Point, int> data = new ConcurrentDictionary<Point, int>();
                var maxcount = Math.Min(xSource.Count, ySource.Count);
                var result = Parallel.For(0, maxcount, new ParallelOptions() { CancellationToken = token, MaxDegreeOfParallelism = maxDegreeOfParallelism }, (i, loop) =>
                {
                    if (loop.IsStopped) return;
                    if (token.IsCancellationRequested) loop.Stop();
                    if (xSource.Count > i && ySource.Count > i)
                    {
                        var x = xGetLocation(xConverter(xSource[i]), xParam);
                        if (double.IsNaN(x) || double.IsInfinity(x)) return;
                        var y = yGetLocation(yConverter(ySource[i]), yParam);
                        if (double.IsNaN(y) || double.IsInfinity(y)) return;
                        var xi = Convert.ToInt32(Math.Floor(x) / pointLength) * pointLength;
                        var yi = Convert.ToInt32(Math.Floor(y) / pointLength) * pointLength;
                        data[new Point(xi, yi)] = i;
                    }
                });
                if (!result.IsCompleted) return null;
                return data;
            }
            catch (OperationCanceledException e)
            {
                Debug.WriteLine(string.Concat("Parallel.For", e.Message));
                return null;
            }
        }

    }
}
